package com.minelx.landisk.filesystem.core.values;

import java.util.*;
import java.util.stream.Stream;

import static java.lang.String.join;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toList;

public class PathDescription {

	private final List<String> steps;

	private PathDescription(List<String> steps) {
		this.steps = steps;
	}

	public String toText() {
		return "/" + join("/", steps);
	}

	public String name() {
		if (steps.isEmpty()) {
			throw new NoSuchElementException("root path has no file name.");
		}
		return steps.get(steps.size() - 1);
	}

	public PathDescription concat(PathDescription another) {
		return PathDescription.from(Stream.concat(steps.stream(), another.steps.stream())
				.collect(toList()));
	}

	public PathDescription child(String childName) {
		List<String> copy = copySteps();
		copy.add(childName);
		return new PathDescription(copy);
	}

	public PathDescription sibling(String name) {
		List<String> copy = copySteps();
		copy.set(copy.size() - 1, name);
		return new PathDescription(copy);
	}

	public PathDescription container() {
		List<String> copy = copySteps();
		copy.remove(copy.size() - 1);
		return new PathDescription(copy);
	}

	private List<String> copySteps() {
		return new ArrayList<>(steps);
	}

	public List<String> steps() {
		return unmodifiableList(steps);
	}

	public static PathDescription from(String path) {
		return PathDescription.from(stepsOf(path));
	}

	private static PathDescription from(List<String> array) {
		Stack<String> result = new Stack<>();
		array.forEach(result::push);
		return new PathDescription(new ArrayList<>(result));
	}

	public static PathDescription root() {
		return new PathDescription(emptyList());
	}

	public static List<String> stepsOf(String path) {
		if (path.equals("/")) {
			return emptyList();
		}
		return Stream.of(removeHeader(path).split("/")).collect(toList());
	}

	private static String removeHeader(String path) {
		if (!path.startsWith("/")) {
			return path;
		}
		return path.substring(1);
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) return true;
		if (o == null || getClass() != o.getClass()) return false;
		PathDescription that = (PathDescription) o;
		return steps.equals(that.steps);
	}

	@Override
	public int hashCode() {
		return Objects.hash(steps);
	}

	@Override
	public String toString() {
		return "PathDescription{" +
				"steps=" + steps +
				'}';
	}
}
